﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Diagnostics;
using OpenGLES;

namespace PFD
{
    public class WorldObject : IDisposable
    {
        /// <summary>
        /// VertexBuffer which stores the geometry data of the object
        /// including texture coordinates
        /// </summary>
        IVertexBuffer vertexBuffer;

        /// <summary>
        /// objectPosition is the position in world space of the object
        /// </summary>
        float[] objectPosition;

        /// <summary>
        /// panelID and name are the IDs taken from the XML file
        /// </summary>
        string panelID;
        string name;

        /// <summary>
        /// activator which controls whether or not this object is active
        /// </summary>
        SimpleActivator activator = new SimpleActivator(true);

        /// <summary>
        /// textures stores all texture layers
        /// </summary>
        MultilayerTexture textures = new MultilayerTexture();

        /// <summary>
        /// besides the position, the transformation between object space
        /// and world space are stored in this list
        /// </summary>
        List<GeometricTransformation> transformations = new List<GeometricTransformation>();



        /// <summary>
        /// constructor
        /// initialize position and vertexBuffer
        /// </summary>
        public WorldObject()
        {
            objectPosition = new float[3] { 0, 0, 0 };
            vertexBuffer = (IVertexBuffer)null;
        }

        public bool LoadFromXML(XmlNode bb, GlobalPoolContainer pool, IRenderDevice rd)
        {
            uint numVertices;

            XmlAttributeCollection attrs = bb.Attributes;

            //  store ID and Name of the object, although it is probably not needed
            panelID = attrs["ID"].Value;
            name = bb.SelectSingleNode("NAME").InnerText;

            //  locate the PRIMITIVETYPE node and extract the number of vertices
            XmlNode ptype = bb.SelectSingleNode("PRIMITIVETYPE");
            numVertices = Convert.ToUInt32(ptype.Attributes["NumVertices"].Value);

            //  determine the type of vertexbuffer to create
            VertexElementType vet = VertexElementType.Position;
            bool bHasNormals = Convert.ToBoolean(ptype.Attributes["HasNormals"].Value);
            bool bHasColors = Convert.ToBoolean(ptype.Attributes["HasColor"].Value);
            bool bHasTexSet1 = Convert.ToBoolean(ptype.Attributes["HasTexSet1"].Value);
            bool bHasTexSet2 = Convert.ToBoolean(ptype.Attributes["HasTexSet2"].Value);

            //  Construct the vertex element type
            if (bHasNormals)
                vet = vet | VertexElementType.Normal;
//            if (bHasColors)
//                vet = vet | VertexElementType.Color;
            if (bHasTexSet1)
                vet = vet | VertexElementType.TextureSet1Coordinates;
            if (bHasTexSet2)
                vet = vet | VertexElementType.TextureSet2Coordinates;

            //  Check if the number of vertices is in a valid range
            if (numVertices < 3) 
                throw new ArgumentOutOfRangeException("NumVertices", "Is less than 3");
            if (numVertices > 1024)
                throw new ArgumentOutOfRangeException("NumVertices", "Is more than 1024");

            //  Create the vertex Buffer object
            vertexBuffer = rd.CreateVertexBuffer(numVertices,vet);

            //  locate the GEOMETRY node in order to construct the vertex buffer
            XmlNodeList geometry = bb.SelectSingleNode("GEOMETRY/VERTICES").ChildNodes;

            int nNumVertices = 0;
            foreach (XmlNode array in geometry)
            {
                switch(array.Name)
                {
                    case    "VERTEX"   :
                        //  Now check if correct number of vertices are included
                        if (nNumVertices >= numVertices)
                            throw new ArgumentOutOfRangeException("Number of vertices is inconsistent", "Number of vertices");
                        
                        //  Do read single texture layer vertices
                        ReadVertexFromXml(array.ChildNodes, nNumVertices++);
                        break;

                    default:
                        throw new ArgumentException("Unrecognized Item found in GEOMETRY/VERTICES", array.Name);
                }
            }

            //  Now load the texture layer(s)
            if (bb.SelectSingleNode("TEXTURE") != null)
            {
                textures.LoadFromXML(bb.SelectSingleNode("TEXTURE"), (int)numVertices, pool, ref vertexBuffer);
            }

            //  Now load the transformations if available
            if (bb.SelectSingleNode("WORLDTRANSFORM") != null)
            {
                //  Iterate through the sequence of transformations
                foreach (XmlNode trafo in bb.SelectNodes("WORLDTRANSFORM/TRANSFORM"))
                {
                    string sTrafoType = trafo.Attributes["TType"].Value.ToLower();
                    if (sTrafoType == "rotate")
                    {
                        GeometricTransformation t = new GeometricTransformation(GeometricTransformation.TransformationType.Rotation);
                        Vector<float>.ReadVectors(trafo, ref t.info, 4);
                        transformations.Add(t);
                    }
                    else if (sTrafoType == "translate")
                    {
                        GeometricTransformation t = new GeometricTransformation(GeometricTransformation.TransformationType.Translation);
                        Vector<float>.ReadVectors(trafo, ref t.info, 3);
                        transformations.Add(t);
                    }
                    else if (sTrafoType == "scale")
                    {
                        GeometricTransformation t = new GeometricTransformation(GeometricTransformation.TransformationType.Scale);
                        Vector<float>.ReadVectors(trafo, ref t.info, 3);
                        transformations.Add(t);
                    }
                    else
                        throw new ArgumentException("Invalid Transformation type", sTrafoType);

                }
            }

            //  Check if this object is activated by a variable
            if (bb.SelectSingleNode("ACTIVATEDBY") != null)
            {
                string activatorVariable;

                activatorVariable = bb.SelectSingleNode("ACTIVATEDBY").InnerText;
                if (!MainForm.sysVars.boolVars.ContainsKey(activatorVariable))
                {
                    MainForm.sysVars.boolVars[activatorVariable] = new List<ISystemVarConnector<bool>>();
                }
                MainForm.sysVars.boolVars[activatorVariable].Add(activator);
            }
            return true;
        }

        unsafe public void Draw(IRenderDevice rd)
        {
            //  if activator variable is specified, check if it may be activated
            if (!activator.active)
                return;

            rd.ResetObjectMatrix();

            //  Perform transformation
            foreach (GeometricTransformation g in transformations)
            {
                g.ApplyTransform(rd);
            }

            rd.DrawObject(DRAW_MODE.TriangleFan, vertexBuffer, textures);
        }

        public bool ReadVertexFromXml(XmlNodeList array, int nIndex)
        {
            foreach (XmlNode node in array)
            {
                switch (node.Name)
                {
                    case    "POSITION":
                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.X, nIndex, node.SelectSingleNode("X"),1.0f);
                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.Y, nIndex, node.SelectSingleNode("Y"), 1.0f);
                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.Z, nIndex, node.SelectSingleNode("Z"), 1.0f);
                        break;
                    case    "NORMAL":
                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.Nx, nIndex, node.SelectSingleNode("X"), 1.0f);
                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.Ny, nIndex, node.SelectSingleNode("Y"), 1.0f);
                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.Nz, nIndex, node.SelectSingleNode("Z"), 1.0f);
                        break;
                    case    "COLOR":
//                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.Red, nIndex, node.SelectSingleNode("X"), 1.0f);
//                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.Green, nIndex, node.SelectSingleNode("Y"), 1.0f);
//                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.Blue, nIndex, node.SelectSingleNode("Z"), 1.0f);
//                        SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.Alpha, nIndex, node.SelectSingleNode("A"), 1.0f);
                        break;
                    case    "TEXCOORD":
                        float normalize = 1.0f;
                        if (node.Attributes["Normalize"] != null)
                            normalize = Helper.ReadFloatFromXmlNodeAttribute(node.Attributes["Normalize"]);

                        if (node.Attributes["Layer"].Value == "TextureSet2")
                        {
                            SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.U2, nIndex, node.SelectSingleNode("U"), normalize);
                            SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.V2, nIndex, node.SelectSingleNode("V"), normalize);
                        }
                        else
                        {
                            SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.U1, nIndex, node.SelectSingleNode("U"), normalize);
                            SystemVariables.ReadParamValue(ref vertexBuffer, VertexElementDimension.V1, nIndex, node.SelectSingleNode("V"), normalize);
                        }
                        break;
                    default:
                        break;
                }
            }
            return true;
        }



        #region IDisposable Members

        public void Dispose()
        {
        }

        #endregion
    }
}
